package edu.ccut.saturn.studio.core.util.xml;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import edu.ccut.saturn.studio.core.exception.XMLException;
import edu.ccut.saturn.studio.core.saturnproperty.StringUtils;

/**
 * 解析XML工具类
 * 
 * @author 郭英杰
 * @author 谷钰
 * 
 * @version 1.0 2008.12.02
 */
public final class SaturnW3CUtil {

	private static final int XML_MAX_LINE = 2000;

	/**
	 * 通过文件路径获得XML Document对像
	 * 
	 * @param path
	 * @return
	 */
	public static Document getDocument(String path) throws XMLException {
		Document document = null;
		InputStream in = null;
		try {

			DocumentBuilder dombuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();

			in = new FileInputStream(path);
			document = dombuilder.parse(in);
		} catch (Exception e) {
			throw new XMLException("Create document failed! In path : [" + path
					+ "]\n", e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return document;
	}

	/**
	 * 通过文件流获得XML Document对像
	 * 
	 * @param input
	 * @return
	 */
	public static Document getDocument(InputStream input) throws XMLException {
		Document document = null;
		try {

			DocumentBuilder dombuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();

			document = dombuilder.parse(input);
		} catch (Exception e) {
			throw new XMLException("Create document failed! " + "]\n", e);
		}

		return document;
	}

	/**
	 * 获得符合名字要求的所有子节点
	 * 
	 * @param node
	 * @param name
	 * @return
	 */
	public static List<Node> getChildrenNode(Node node) {
		List<Node> needChildren = new ArrayList<Node>();

		if (node != null) {
			NodeList children = node.getChildNodes();

			if (children != null) {
				for (int i = 0; i < children.getLength(); ++i) {
					Node child = (Node) children.item(i);

					if (child != null
							&& child.getNodeType() == Node.ELEMENT_NODE) {

						needChildren.add(child);
					}
				}
			}
		}

		return needChildren;
	}

	/**
	 * 获得符合名字要求的所有子节点
	 * 
	 * @param node
	 * @param name
	 * @return
	 */
	public static List<Node> getChildrenByName(Node node, String name) {
		List<Node> needChildren = new ArrayList<Node>();

		if (node != null) {
			NodeList children = node.getChildNodes();

			if (children != null) {
				for (int i = 0; i < children.getLength(); ++i) {
					Node child = (Node) children.item(i);

					if (child != null
							&& child.getNodeType() == Node.ELEMENT_NODE) {

						Element element = (Element) child;
						if (element.getTagName().equals(name)) {
							needChildren.add(child);
						}
					}
				}
			}
		}

		return needChildren;
	}

	/**
	 * 获得符合名字要求的第一个子节点
	 * 
	 * @param node
	 * @param name
	 * @return
	 */
	public static Node getFirstChildByName(Node node, String name) {
		List<Node> needChildren = getChildrenByName(node, name);

		if (needChildren != null && !needChildren.isEmpty()) {
			return needChildren.get(0);
		}

		return null;
	}

	/**
	 * 根据字节点名称和对应属性值获得节点
	 * 
	 * @param node
	 * @param childNodeName
	 * @param name
	 * @param value
	 * @return
	 */
	public static Node getChildByNameAndAttribute(Node node,
			String childNodeName, String name, String value) {

		List<Node> children = getChildrenByName(node, childNodeName);

		if (children != null && StringUtils.isNotEmpty(value)) {
			for (Node child : children) {
				if (value.equals(getNodeAttributeValue(child, name))) {
					return child;
				}
			}
		}

		return null;
	}

	/**
	 * 通过XML NODE对像，获得指定节点属性值
	 * 
	 * @param node
	 * @param key
	 * @return
	 */
	public static String getNodeAttributeValue(Node node, String key) {
		if (node != null && StringUtils.isNotEmpty(key)) {
			NamedNodeMap namedNodeMap = node.getAttributes();

			if (namedNodeMap != null) {
				Node attribute = namedNodeMap.getNamedItem(key);
				if (attribute != null) {
					return attribute.getNodeValue();
				}
			}
		}

		return "";
	}
	
	
	
	/**
	 * 通过XML NODE 对象 ，获得指定的子节点的值
	 * 
	 * @param node
	 * @param key
	 * @return
	 */
	public static String getChildNodeValue(Node node, String key) {
		if (node != null && StringUtils.isNotEmpty(key)) {
			List<Node> children = getChildrenByName(node, key);

			for (Node child : children) {
				if (key.equals(child.getNodeName())) {
					return getNodeValue(child);
				}
			}
		}

		return "";
	}

	public static String getNodeValue(Node node) {
		if (node != null && node.getFirstChild() != null) {
			String value = node.getFirstChild().getNodeValue();

			return value.replaceAll("[\t ]*\n[\t ]*", " ").replaceAll(
					"[\t ][\t ]+", " ").trim();
		}

		return "";
	}

	/**
	 * 
	 * 创建子节点
	 * 
	 * @param node
	 *            在此节点上添加子节点
	 * @param childElementName
	 *            子节点名称
	 * @param textValue
	 *            值
	 * @return
	 */
	public static Node createChildNode(Node node, String childElementName,
			String textValue) {

		Node child = null;

		if (node != null) {
			Document document = node.getOwnerDocument();

			child = document.createElement(childElementName);
			if (StringUtils.isNotEmpty(textValue)) {
				child.appendChild(document.createTextNode(textValue));
			}

			node.appendChild(child);
		}

		return child;
	}
	
	/**
	 * 修改子节点的值
	 * 
	 * @param node
	 * 			父节点
	 * @param childElementName
	 * 			子节点
	 * @param textValue
	 * 			子节点的值
	 */
	public static void setChildNodeValue(Node node, String childElementName,
			String textValue) {
		Node child = null;

		if (node != null) {
			child = getFirstChildByName(node, childElementName);

			if (child != null) {
				String value = getNodeValue(child);

				if (!value.equals(textValue)) {
					node.removeChild(child);
					
					createChildNode(node, childElementName, textValue);
				}
			}
		}
	}

	/**
	 * 创建节点属性
	 * 
	 * @param node
	 *            在此节点上添加属性节点
	 * @param tagName
	 *            属性节点名称
	 * @param textValue
	 *            值
	 * @return
	 */
	public static void createAttribute(Node node, String attrName, String value) {

		if (node != null && StringUtils.isNotEmpty(attrName)
//				&& StringUtils.isNotEmpty(value)) {
		) {
			String oldAttributeValue = getNodeAttributeValue(node, attrName);

			if (!value.equals(oldAttributeValue)||(oldAttributeValue == "")) {
				Document document = node.getOwnerDocument();

				Node tagAttr = document.createAttribute(attrName);
				tagAttr.setNodeValue(value);

				if (node.getAttributes() != null) {
					node.getAttributes().setNamedItem(tagAttr);
				}
			}
		}
	}

	/**
	 * 把xml写到指定路径下的文件中
	 * 
	 * @param doc
	 * @param filePath
	 * @throws XMLException
	 */
	public static void writeDocument(Document doc, String filePath)
			throws XMLException {

		FileOutputStream out = null;
		try {
			out = new FileOutputStream(filePath);
			writeDocument(doc, out);
		} catch (Exception e) {
			throw new XMLException(e);
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 把xml写到指定out设备上去
	 * 
	 * @param doc
	 * @param out
	 * @throws XMLException
	 */
	public static void writeDocument(Document doc, OutputStream out)
			throws XMLException {
		OutputFormat format = new OutputFormat(doc, "UTF-8", true);

		try {
			XMLSerializer serializercom = new XMLSerializer(out, format);
			serializercom.serialize(doc);
		} catch (IOException e) {
			throw new XMLException("Write xml faild!", e);
		}
	}

	/**
	 * 清除所有元素
	 * 
	 * @param document
	 */
	public static void clearDocument(Document document) {
		Node root = document.getDocumentElement();
		removeChildren(root);
	}

	/**
	 * 清除所有元素
	 * 
	 * @param document
	 */
	public static void removeChildren(Node root) {
		Node firstNode = root.getFirstChild();

		Node node = firstNode;

		while (node != null) {
			Node nextNode = node.getNextSibling();
			root.removeChild(node);
			node = nextNode;
		}
	}

	/**
	 * 格式化指定节点的XML
	 * 
	 * @param node
	 */
	@SuppressWarnings("restriction")
	public static void format(Node node) {
		if (node != null) {
			org.eclipse.wst.xml.core.internal.provisional.format.ElementNodeFormatter formatter = new org.eclipse.wst.xml.core.internal.provisional.format.ElementNodeFormatter();

			formatter.getFormatPreferences().setClearAllBlankLines(true);
			formatter.getFormatPreferences().setLineWidth(XML_MAX_LINE);
			formatter.format(node);
		}
	}
	
}
